package cn.com.zero.job.core;

import cn.com.zero.base.service.BaseService;
import cn.com.zero.job.mapper.XxlJobGroupMapper;
import cn.com.zero.job.mapper.XxlJobInfoMapper;
import cn.com.zero.job.mapper.ZeroJobInfoMapper;
import cn.com.zero.job.model.XxlJobGroup;
import cn.com.zero.job.model.XxlJobInfo;
import cn.com.zero.job.model.ZeroJobInfo;
import cn.hutool.core.util.StrUtil;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.util.Collection;
import java.util.Date;
import java.util.List;

/**
 * @author zhoupeng
 * @version 1.0.0
 * @Description
 * @createTime 2022-07-18
 */
@Service
public class ZeroJobService extends BaseService {

    @Value("${xxl.job.executor.appname:${spring.application.name}}")
    private String appName;

    @Resource
    private ZeroJobInfoMapper zeroMapper;
    @Resource
    private XxlJobInfoMapper xxlMapper;
    @Resource
    private XxlJobGroupMapper xxlGroupMapper;

    /**
     * 更新任务信息到数据库
     */
    @Transactional
    public void updateJobs(Collection<ZeroJob> localList) {
        // 如果表不存在则创建表
        zeroMapper.createTableIfNotExist();

        int groupId;
        // 找到是否存在 Group
        List<XxlJobGroup> groupList = xxlGroupMapper.findByApp(appName);
        if (groupList.isEmpty()) {
            logger.info("创建 {} 执行器", appName);
            XxlJobGroup group = new XxlJobGroup();
            group.setAppname(appName);
            group.setTitle("zero-job");
            group.setUpdateTime(new Date());
            groupId = xxlGroupMapper.insert(group);

        } else {
            groupId = groupList.get(0).getId();
        }

        List<ZeroJobInfo> dbList = zeroMapper.findByApp(appName);

        out:
        for (ZeroJob localJob : localList) {
            for (ZeroJobInfo info : dbList) {
                if (StrUtil.equals(localJob.name(), info.getName())) {
                    if (info.getXxlJobInfo() == null) {
                        // 如果关联的任务不存在，则添加
                        logger.info("{} 任务已被删除，重新创建", localJob.name());
                        XxlJobInfo newJobInfo = addXxlJob(groupId, localJob);
                        info.setXxlId(newJobInfo.getId());
                        info.setVersion(localJob.version());
                        zeroMapper.update(info);
                    } else if (localJob.version() > info.getVersion()) {
                        // 如果本地任务版本增加，则更新任务信息
                        logger.info("更新 {} 任务", localJob.name());
                        updateXxlJob(info.getXxlJobInfo(), localJob);
                        info.setVersion(localJob.version());
                        zeroMapper.update(info);
                    }
                    // 处理下一个任务，
                    continue out;
                }
            }
            // 任务第一次创建，新增任务信息
            XxlJobInfo xxlJobInfo = addXxlJob(groupId, localJob);
            ZeroJobInfo zeroJobInfo = new ZeroJobInfo();
            zeroJobInfo.setName(localJob.name());
            zeroJobInfo.setVersion(localJob.version());
            zeroJobInfo.setXxlId(xxlJobInfo.getId());
            zeroJobInfo.setApp(appName);
            zeroMapper.insert(zeroJobInfo);
        }
    }


    /**
     * 根据任务实现添加 xxlJob
     *
     * @param zeroJob 任务实现
     */
    @Transactional
    public XxlJobInfo addXxlJob(int groupId, ZeroJob zeroJob) {
        logger.info("创建 {} 任务", zeroJob.name());
        XxlJobInfo xxlJobInfo = zeroJob.getXxlInfo(appName);
        xxlJobInfo.setJobGroup(groupId);
        xxlJobInfo.setAddTime(new Date());
        xxlJobInfo.setUpdateTime(new Date());
        xxlJobInfo.setGlueUpdatetime(new Date());
        xxlMapper.insert(xxlJobInfo);

        return xxlJobInfo;
    }

    /**
     * 根据任务实现更新 xxlJob
     *
     * @param oldInfo 当前任务信息
     * @param zeroJob 本地任务实现
     */
    @Transactional
    public void updateXxlJob(XxlJobInfo oldInfo, ZeroJob zeroJob) {
        XxlJobInfo newInfo = zeroJob.getXxlInfo(appName);
        newInfo.setId(oldInfo.getId());
        newInfo.setJobGroup(oldInfo.getJobGroup());
        newInfo.setAddTime(oldInfo.getAddTime());
        newInfo.setUpdateTime(new Date());
        newInfo.setGlueUpdatetime(new Date());
        newInfo.setTriggerLastTime(oldInfo.getTriggerLastTime());
        newInfo.setTriggerNextTime(oldInfo.getTriggerNextTime());
        xxlMapper.update(newInfo);
    }

    /**
     * 查找指定名称任务
     */
    private ZeroJobInfo findJobByName(String name) {
        ZeroJobInfo zeroJobInfo = zeroMapper.findByAppAndName(appName, name);
        if (zeroJobInfo == null) {
            logger.error("未找到 {}.{} 任务", appName, name);
        }
        return zeroJobInfo;
    }

    /**
     * 更新任务状态
     *
     * @param name   任务名称
     * @param status 状态
     */
    @Transactional
    public void updateJobStatus(String name, int status) {
        ZeroJobInfo zeroJobInfo = findJobByName(name);
        if (zeroJobInfo != null) {
            XxlJobInfo xxlJobInfo = zeroJobInfo.getXxlJobInfo();
            if (xxlJobInfo == null) {
                logger.error("任务 {}.{} 已被删除，启动失败", appName, name);
            } else if (xxlJobInfo.getTriggerStatus() != status) {
                xxlJobInfo.setTriggerStatus(status);
                xxlMapper.update(xxlJobInfo);
                logger.info("任务 {}.{} 已{}", appName, name, status == XxlJobInfo.STATUS_START ? "启动" : "停止");
            }
        }
    }

    /**
     * 启动任务
     *
     * @param name 任务名称
     */
    public void startJob(String name) {
        updateJobStatus(name, XxlJobInfo.STATUS_START);
    }

    /**
     * 停止任务
     *
     * @param name 任务名称
     */
    public void stopJob(String name) {
        updateJobStatus(name, XxlJobInfo.STATUS_STOP);
    }
}
